

	var _DeflateTreeDesc =  Core.Class.subclass({
		classname: "_DeflateTreeDesc",
		initialize: function(){
		    this.dyn_tree = null;
		    this.static_tree = null;
		    this.extra_bits = null;
		    this.extra_base = 0;
		    this.elems = 0;
		    this.max_length = 0;
		    this.max_code = 0;
		}
	});
	
	var _DeflateConfiguration = Core.Class.subclass({
		classname: "_DeflateConfiguration",
		initialize: function(a, b, c, d) {
		    this.good_length = a;
		    this.max_lazy = b;
		    this.nice_length = c;
		    this.max_chain = d;
		}
	});

	var _DeflateBuffer = Core.Class.subclass({
		classname: "_DeflateBuffer",
		initialize: function(outBufSize) {
		    this.next = null;
		    this.len = 0;
		    this.ptr = new Array(outBufSize);
		    this.off = 0;
		}
	});





var _DeflateCT= Core.Class.subclass({
	classname: '_DeflateCT',
	initialize: function(){
	    this.fc = 0;
	    this.dl = 0;
	}
});

exports.GAZip = Core.Class.subclass({
	classname: 'GAZip',
	initialize: function(){

		this._WSIZE = 32768;
		this._STORED_BLOCK = 0;
		this._STATIC_TREES = 1;
		this._DYN_TREES    = 2;
		this.MAGIC = '\x08\x00';

		this._DEFAULT_LEVEL = 6;
		this._FULL_SEARCH = true;
		this._INBUFSIZ = 32768;
		this._INBUF_EXTRA = 64;
		this._OUTBUFSIZ = 1024 * 8;
		this._window_size = 2 * this._WSIZE;
		this._MIN_MATCH = 3;
		this._MAX_MATCH = 258;
		this._BITS = 16;
		this._LIT_BUFSIZE = 0x2000;
		this._HASH_BITS = 13;
		if(this._LIT_BUFSIZE > this._INBUFSIZ){
		    console.log("error: _INBUFSIZ is too small");
		}
		if((this._WSIZE<<1) > (1<<this._BITS)){
		    console.log("error: _WSIZE is too large");
		}
		if(this._HASH_BITS > this._BITS-1){
			console.log("error: this._HASH_BITS is too large");
		}
		if(this._HASH_BITS < 8 || this._MAX_MATCH != 258){
			console.log("error: Code too clever");
		}
		this._DIST_BUFSIZE = this._LIT_BUFSIZE;
		this._HASH_SIZE = 1 << this._HASH_BITS;
		this._HASH_MASK = this._HASH_SIZE - 1;
		this._WMASK = this._WSIZE - 1;
		this._NIL = 0;
		this._TOO_FAR = 4096;
		this._MIN_LOOKAHEAD = this._MAX_MATCH + this._MIN_MATCH + 1;
		this._MAX_DIST = this._WSIZE - this._MIN_LOOKAHEAD;
		this._SMALLEST = 1;
		this._MAX_BITS = 15;
		this._MAX_BL_BITS = 7;
		this._LENGTH_CODES = 29;
		this._LITERALS =256;
		this._END_BLOCK = 256;
		this._L_CODES = this._LITERALS + 1 + this._LENGTH_CODES;
		this._D_CODES = 30;
		this._BL_CODES = 19;
		this._REP_3_6 = 16;
		this._REPZ_3_10 = 17;
		this._REPZ_11_138 = 18;
		this._HEAP_SIZE = 2 * this._L_CODES + 1;
		this._H_SHIFT = parseInt((this._HASH_BITS + this._MIN_MATCH - 1) /
					   this._MIN_MATCH);

		this._free_queue;
		this._qhead;
		this._qtail;
		this._initflag;
		this._outbuf = null;
		this._outcnt;
		this._outoff;
		this._complete;
		this._window;
		this._d_buf;
		this._l_buf;
		this._prev;
		this._bi_buf;
		this._bi_valid;
		this._block_start;
		this._ins_h;
		this._hash_head;
		this._prev_match;
		this._match_available;
		this._match_length;
		this._prev_length;
		this._strstart;
		this._match_start;
		this._eofile;
		this._lookahead;
		this._max_chain_length;
		this._max_lazy_match;
		this._compr_level;
		this._good_match;
		this._nice_match;
		this._dyn_ltree;
		this._dyn_dtree;
		this._static_ltree;
		this._static_dtree;
		this._bl_tree;
		this._l_desc;
		this._d_desc;
		this._bl_desc;
		this._bl_count;
		this._heap;
		this._heap_len;
		this._heap_max;
		this._depth;
		this._length_code;
		this._dist_code;
		this._base_length;
		this._base_dist;
		this._flag_buf;
		this._last_lit;
		this._last_dist;
		this._last_flags;
		this._flags;
		this._flag_bit;
		this._opt_len;
		this._static_len;
		this._deflate_data;
		this._deflate_pos;
		this._Buf_size = 16;
		
		
		this._extra_lbits = new Array(
		    0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0);
		this._extra_dbits = new Array(
		    0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13);
		this._extra_blbits = new Array(
		    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7);
		this._bl_order = new Array(
		    16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15);
		this._configuration_table = new Array(
			new _DeflateConfiguration(0,    0,   0,    0),
			new _DeflateConfiguration(4,    4,   8,    4),
			new _DeflateConfiguration(4,    5,  16,    8),
			new _DeflateConfiguration(4,    6,  32,   32),
			new _DeflateConfiguration(4,    4,  16,   16),
			new _DeflateConfiguration(8,   16,  32,   32),
			new _DeflateConfiguration(8,   16, 128,  128),
			new _DeflateConfiguration(8,   32, 128,  256),
			new _DeflateConfiguration(32, 128, 258, 1024),
			new _DeflateConfiguration(32, 258, 258, 4096));

		
		
		
	},
	compress: function(contents){
		return this._deflate(contents);
	},
	_deflate: function(str) {
	    var i; 
	    var j;
	    this._deflate_data = str;
	    this._deflate_pos = 0;
		var level = this._DEFAULT_LEVEL;
		
	    this._deflate_start(level);

	    var buff = new Array(1024);
	    var aout = [];
	    while((i = this._deflate_internal(buff, 0, buff.length)) > 0) {
			var cbuf = new Array(i);
			for(j = 0; j < i; j++){
			    cbuf[j] = String.fromCharCode(buff[j]);
			}
			aout[aout.length] = cbuf.join("");
	    }
	    this._deflate_data = null;
	    return aout.join("");
	},
	
	
	_deflate_start: function(level) {
	    var i;

	    if(!level){
	    	level = this._DEFAULT_LEVEL;
	    }
	    else if(level < 1){
	    	level = 1;
	    }
	    else if(level > 9){
	    	level = 9;
	    }

	    this._compr_level = level;
	    this._initflag = false;
	    this._eofile = false;
	    if(this._outbuf != null)
		return;

	    this._free_queue = null;
	    this._qhead = null;
	    this._qtail = null;
	    this._outbuf = new Array(this._OUTBUFSIZ);
	    this._window = new Array(this._window_size);
	    this._d_buf = new Array(this._DIST_BUFSIZE);
	    this._l_buf = new Array(this._INBUFSIZ + this._INBUF_EXTRA);
	    
	    this._prev = new Array(1 << this._BITS);
	    this._dyn_ltree = new Array(this._HEAP_SIZE);
	    
	    
	    
	    for(i = 0; i < this._HEAP_SIZE; i++){
	    	this._dyn_ltree[i] = new _DeflateCT();
	    }
	    
	    
	    this._dyn_dtree = new Array(2*this._D_CODES+1);
	    
	    for(i = 0; i < 2*this._D_CODES+1; i++){
	    	this._dyn_dtree[i] = new _DeflateCT();
	    }
	    
	    this._static_ltree = new Array(this._L_CODES+2);
	    
	    for(i = 0; i < this._L_CODES+2; i++){
	    	this._static_ltree[i] = new _DeflateCT();
	    }
	    
	    this._static_dtree = new Array(this._D_CODES);
	    for(i = 0; i < this._D_CODES; i++){
	    	this._static_dtree[i] = new _DeflateCT();
	    }
	    
	    this._bl_tree = new Array(2*this._BL_CODES+1);
	    for(i = 0; i < 2*this._BL_CODES+1; i++){
	    	this._bl_tree[i] = new _DeflateCT();
	    }
	    
	    this._l_desc = new _DeflateTreeDesc();
	    this._d_desc = new _DeflateTreeDesc();
	    this._bl_desc = new _DeflateTreeDesc();
	    this._bl_count = new Array(this._MAX_BITS+1);
	    this._heap = new Array(2*this._L_CODES+1);
	    this._depth = new Array(2*this._L_CODES+1);
	    this._length_code = new Array(this._MAX_MATCH-this._MIN_MATCH+1);
	    this._dist_code = new Array(512);
	    this._base_length = new Array(this._LENGTH_CODES);
	    this._base_dist = new Array(this._D_CODES);
	    this._flag_buf = new Array(parseInt(this._LIT_BUFSIZE / 8));
	},
	
	_deflate_internal: function(buff, off, buff_size) {
	    var n;

	    if(!this._initflag)
	    {
			this._init_deflate();
			this._initflag = true;
			if(this._lookahead == 0) {
				this._complete = true;
			    return 0;
			}
	    }

	    if((n = this._qcopy(buff, off, buff_size)) == buff_size){
	    	return buff_size;
	    }

	    if(this._complete){
	    	return n;
	    }

	    if(this._compr_level <= 3) {
	    	this._deflate_fast();
	    }
	    else{
	    	this._deflate_better();
	    }
	    if(this._lookahead == 0) {
			if(this._match_available != 0){
			    this._ct_tally(0, this._window[this._strstart - 1] & 0xff);
			}
			this._flush_block(1);
			this._complete = true;
	    }
	    return n + this._qcopy(buff, n + off, buff_size - n);
	},
	
	_init_deflate: function() {
	    if(this._eofile){
	    	return;
	    }
	    this._bi_buf = 0;
	    this._bi_valid = 0;
	    this._ct_init();
	    this._lm_init();

	    this._qhead = null;
	    this._outcnt = 0;
	    this._outoff = 0;

	    if(this._compr_level <= 3)
	    {
	    	this._prev_length = this._MIN_MATCH - 1;
	    	this._match_length = 0;
	    }
	    else
	    {
	    	this._match_length = this._MIN_MATCH - 1;
	    	this._match_available = 0;
	    }

	    this._complete = false;
	},
	
	_qcopy: function(buff, off, buff_size) {
	    var n;
	    var i;
	    var j;

	    n = 0;
	    while(this._qhead != null && n < buff_size){
			i = buff_size - n;
			if(i > this._qhead.len){
			    i = this._qhead.len;
			}
			for(j = 0; j < i; j++){
			    buff[off + n + j] = this._qhead.ptr[this._qhead.off + j];
			}
			
			this._qhead.off += i;
			this._qhead.len -= i;
			n += i;
			if(this._qhead.len == 0) {
			    var p;
			    p = this._qhead;
			    this._qhead = this._qhead.next;
			    this._reuse_queue(p);
			}
	    }

	    if(n == buff_size){
	    	return n;
	    }

	    if(this._outoff < this._outcnt) {
			i = buff_size - n;
			if(i > this._outcnt - this._outoff){
			    i = this._outcnt - this._outoff;
			}
			for(j = 0; j < i; j++){
			    buff[off + n + j] = this._outbuf[this._outoff + j];
			}
			this._outoff += i;
			n += i;
			if(this._outcnt == this._outoff){
				this._outcnt = this._outoff = 0;
			}
	    }
	    return n;
	},
	
	_reuse_queue: function(p) {
	    p.next = this._free_queue;
	    this._free_queue = p;
	},
	
	_deflate_fast: function() {
	    while(this._lookahead != 0 && this._qhead == null) {
			var flush;

			this._INSERT_STRING();
			if(this._hash_head != this._NIL &&
					this._strstart - this._hash_head <= this._MAX_DIST) {
				this._match_length = this._longest_match(this._hash_head);
			    if(this._match_length > this._lookahead){
			    	this._match_length = this._lookahead;
			    }
			}
			if(this._match_length >= this._MIN_MATCH) {
	
			    flush = this._ct_tally(this._strstart - this._match_start,
			    		this._match_length - this._MIN_MATCH);
			    this._lookahead -= this._match_length;
			    if(this._match_length <= this._max_lazy_match) {
				    this._match_length--;
					do {
						this._strstart++;
						this._INSERT_STRING();
					} while(--this._match_length != 0);
					this._strstart++;
			    } else {
			    	this._strstart += this._match_length;
			    	this._match_length = 0;
			    	this._ins_h = this._window[this._strstart] & 0xff;
			    	this._ins_h = ((this._ins_h<<this._H_SHIFT) ^ (this._window[this._strstart + 1] & 0xff)) & this._HASH_MASK;
		
			    }
			} else {
			    flush = this._ct_tally(0, this._window[this._strstart] & 0xff);
			    this._lookahead--;
			    this._strstart++;
			}
			if(flush) {
			    this._flush_block(0);
			    this._block_start = this._strstart;
			}
			while(this._lookahead < this._MIN_LOOKAHEAD && !this._eofile){
				this._fill_window();
			}
	    }
	},
	
	_INSERT_STRING: function() {
	    this._ins_h = ((this._ins_h << this._H_SHIFT)
			 ^ (this._window[this._strstart + this._MIN_MATCH - 1] & 0xff))
		& this._HASH_MASK;
	    this._hash_head = this._head1(this._ins_h);
	    this._prev[this._strstart & this._WMASK] = this._hash_head;
	    this._head2(this._ins_h, this._strstart);
	},
	
	_head1: function(i) {
	    return this._prev[this._WSIZE + i];
	},

	_head2: function(i, val) {
	    return this._prev[this._WSIZE + i] = val;
	},

	_deflate_better: function() {
	    while(this._lookahead != 0 && this._qhead == null) {
	    	this._INSERT_STRING();
	    	this._prev_length = this._match_length;
	    	this._prev_match = this._match_start;
	    	this._match_length = this._MIN_MATCH - 1;
	
			if(this._hash_head != this._NIL &&
					this._prev_length < this._max_lazy_match &&
					this._strstart - this._hash_head <= this._MAX_DIST) {
				this._match_length = this._longest_match(this._hash_head);
			    if(this._match_length > this._lookahead){
			    	this._match_length = this._lookahead;
			    }
	
			    if(this._match_length == this._MIN_MATCH &&
			       this._strstart - this._match_start > this._TOO_FAR) {
			    	this._match_length--;
			    }
			}
			if(this._prev_length >= this._MIN_MATCH &&
			   this._match_length <= this._prev_length) {
			    var flush;
	
			    flush = this._ct_tally(this._strstart - 1 - this._prev_match,
						 this._prev_length - this._MIN_MATCH);
	
			    this._lookahead -= this._prev_length - 1;
			    this._prev_length -= 2;
			    do {
					this._strstart++;
					this._INSERT_STRING();
			    } while(--this._prev_length != 0);
			    this._match_available = 0;
			    this._match_length = this._MIN_MATCH - 1;
			    this._strstart++;
			    if(flush) {
					this._flush_block(0);
					this._block_start = this._strstart;
			    }
			} else if(this._match_available != 0) {
			    if(this._ct_tally(0, this._window[this._strstart - 1] & 0xff)) {
					this._flush_block(0);
					this._block_start = this._strstart;
			    }
			    this._strstart++;
			    this._lookahead--;
			} else {
			    this._match_available = 1;
			    this._strstart++;
			    this._lookahead--;
			}
			while(this._lookahead < this._MIN_LOOKAHEAD && !this._eofile){
			    this._fill_window();
			}
	    }
	},

	_ct_tally: function(
		dist,
		lc) { 
	    this._l_buf[this._last_lit++] = lc;
	    if(dist == 0) {
			this._dyn_ltree[lc].fc++;
	    } else {
		
			dist--;	
	
			this._dyn_ltree[this._length_code[lc]+this._LITERALS+1].fc++;
			this._dyn_dtree[this._D_CODE(dist)].fc++;
	
			this._d_buf[this._last_dist++] = dist;
			this._flags |= this._flag_bit;
	    }
	    this._flag_bit <<= 1;

	
	    if((this._last_lit & 7) == 0) {
		this._flag_buf[this._last_flags++] = this._flags;
		this._flags = 0;
		this._flag_bit = 1;
	    }
	    if(this._compr_level > 2 && (this._last_lit & 0xfff) == 0) {
		var out_length = this._last_lit * 8;
		var in_length = this._strstart - this._block_start;
		var dcode;

		for(dcode = 0; dcode < this._D_CODES; dcode++) {
		    out_length += this._dyn_dtree[dcode].fc * (5 + this._extra_dbits[dcode]);
		}
		out_length >>= 3;
		if(this._last_dist < parseInt(this._last_lit/2) &&
		   out_length < parseInt(in_length/2))
		    return true;
	    }
	    return (this._last_lit == this._LIT_BUFSIZE-1 ||
		    this._last_dist == this._DIST_BUFSIZE);
	},
	

	_flush_block: function(eof) {
	    var opt_lenb;
	    var static_lenb;
	    var max_blindex;
	    var stored_len;	

	    stored_len = this._strstart - this._block_start;
	    this._flag_buf[this._last_flags] = this._flags; 

	    
	    this._build_tree(this._l_desc);

	    this._build_tree(this._d_desc);

	    max_blindex = this._build_bl_tree();

	    opt_lenb	= (this._opt_len   +3+7)>>3;
	    static_lenb = (this._static_len+3+7)>>3;

	    if(static_lenb <= opt_lenb)
		opt_lenb = static_lenb;
	    if(stored_len + 4 <= opt_lenb 
	       && this._block_start >= 0) {
		var i;
		this._send_bits((this._STORED_BLOCK<<1)+eof, 3);
		this._bi_windup();		
		this._put_short(stored_len);
		this._put_short(~stored_len);

		for(i = 0; i < stored_len; i++)
		    this._put_byte(this._window[this._block_start + i]);

	    } else if(static_lenb == opt_lenb) {
		this._send_bits((this._STATIC_TREES<<1)+eof, 3);
		this._compress_block(this._static_ltree, this._static_dtree);
	    } else {
		this._send_bits((this._DYN_TREES<<1)+eof, 3);
		this._send_all_trees(this._l_desc.max_code+1,
				   this._d_desc.max_code+1,
				   max_blindex+1);
		this._compress_block(this._dyn_ltree, this._dyn_dtree);
	    }

	    this._init_block();

	    if(eof != 0)
		this._bi_windup();
	},

	_ct_init: function() {
	    var n;
	    var bits;	
	    var length;	
	    var code;	
	    var dist;	

	    if(this._static_dtree[0].dl != 0){
	    	return; 
	    }

	    this._l_desc.dyn_tree		= this._dyn_ltree;
	    this._l_desc.static_tree	= this._static_ltree;
	    this._l_desc.extra_bits	= this._extra_lbits;
	    this._l_desc.extra_base	= this._LITERALS + 1;
	    this._l_desc.elems		= this._L_CODES;
	    this._l_desc.max_length	= this._MAX_BITS;
	    this._l_desc.max_code		= 0;

	    this._d_desc.dyn_tree		= this._dyn_dtree;
	    this._d_desc.static_tree	= this._static_dtree;
	    this._d_desc.extra_bits	= this._extra_dbits;
	    this._d_desc.extra_base	= 0;
	    this._d_desc.elems		= this._D_CODES;
	    this._d_desc.max_length	= this._MAX_BITS;
	    this._d_desc.max_code		= 0;

	    this._bl_desc.dyn_tree	= this._bl_tree;
	    this._bl_desc.static_tree	= null;
	    this._bl_desc.extra_bits	= this._extra_blbits;
	    this._bl_desc.extra_base	= 0;
	    this._bl_desc.elems		= this._BL_CODES;
	    this._bl_desc.max_length	= this._MAX_BL_BITS;
	    this._bl_desc.max_code	= 0;

	  
	    length = 0;
	    for(code = 0; code < this._LENGTH_CODES-1; code++) {
			this._base_length[code] = length;
			for(n = 0; n < (1<<this._extra_lbits[code]); n++){
				this._length_code[length++] = code;
			}
	    }

	  
	    this._length_code[length-1] = code;

	    
	    dist = 0;
	    for(code = 0 ; code < 16; code++) {
			this._base_dist[code] = dist;
			for(n = 0; n < (1<<this._extra_dbits[code]); n++) {
			    this._dist_code[dist++] = code;
			}
	    }
	    dist >>= 7; 
	    for( ; code < this._D_CODES; code++) {
			this._base_dist[code] = dist << 7;
			for(n = 0; n < (1<<(this._extra_dbits[code]-7)); n++){
				this._dist_code[256 + dist++] = code;
			}
	    }

	    
	    for(bits = 0; bits <= this._MAX_BITS; bits++){
	    	this._bl_count[bits] = 0;
	    }
	    n = 0;
	    while(n <= 143) { this._static_ltree[n++].dl = 8; this._bl_count[8]++; }
	    while(n <= 255) { this._static_ltree[n++].dl = 9; this._bl_count[9]++; }
	    while(n <= 279) { this._static_ltree[n++].dl = 7; this._bl_count[7]++; }
	    while(n <= 287) { this._static_ltree[n++].dl = 8; this._bl_count[8]++; }
	  
	    this._gen_codes(this._static_ltree, this._L_CODES + 1);

	  
	    for(n = 0; n < this._D_CODES; n++) {
			this._static_dtree[n].dl = 5;
			this._static_dtree[n].fc = this._bi_reverse(n, 5);
	    }

	    this._init_block();
	},

	_lm_init: function() {
	    var j;

	  
	    for(j = 0; j < this._HASH_SIZE; j++){
	    	this._prev[this._WSIZE + j] = 0;
	    }


	    this._max_lazy_match = this._configuration_table[this._compr_level].max_lazy;
	    this._good_match     = this._configuration_table[this._compr_level].good_length;
	    if(!this._FULL_SEARCH){
	    	this._nice_match = this._configuration_table[this._compr_level].nice_length;
	    }
	    this._max_chain_length = this._configuration_table[this._compr_level].max_chain;

	    this._strstart = 0;
	    this._block_start = 0;

	    this._lookahead = this._read_buff(this._window, 0, 2 * this._WSIZE);
	    if(this._lookahead <= 0) {
			this._eofile = true;
			this._lookahead = 0;
			return;
	    }
	    this._eofile = false;
	 
	    while(this._lookahead < this._MIN_LOOKAHEAD && !this._eofile){
	    	this._fill_window();
	    }
	    this._ins_h = 0;
	    for(j = 0; j < this._MIN_MATCH - 1; j++) {
		this._ins_h = ((this._ins_h << this._H_SHIFT) ^ (this._window[j] & 0xff)) & this._HASH_MASK;
	    }
	},
	

	_fill_window: function() {
	    var n;
	    var m;


	    var more = this._window_size - this._lookahead - this._strstart;


	    if(more == -1) {
	    	more--;
	    } else if(this._strstart >= this._WSIZE + this._MAX_DIST) {
	
			for(n = 0; n < this._WSIZE; n++){
			    this._window[n] = this._window[n + this._WSIZE];
			}
			this._match_start -= this._WSIZE;
			this._strstart    -= this._WSIZE; 
			this._block_start -= this._WSIZE;
	
			for(n = 0; n < this._HASH_SIZE; n++) {
			    m = this._head1(n);
			    this._head2(n, m >= this._WSIZE ? m - this._WSIZE : this._NIL);
			}
			for(n = 0; n < this._WSIZE; n++) {
			   
			    m = this._prev[n];
			    this._prev[n] = (m >= this._WSIZE ? m - this._WSIZE : this._NIL);
			}
			more += this._WSIZE;
	    }
	    
	    if(!this._eofile) {
		n = this._read_buff(this._window, this._strstart + this._lookahead, more);
		if(n <= 0)
		    this._eofile = true;
		else
		    this._lookahead += n;
	    }
	},
	
	_longest_match: function(cur_match) {
	    var chain_length = this._max_chain_length; 
	    var scanp = this._strstart; 
	    var matchp;		
	    var len;		
	    var best_len = this._prev_length;	

	    var limit = (this._strstart > this._MAX_DIST ? this._strstart - this._MAX_DIST : this._NIL);

	    var strendp = this._strstart + this._MAX_MATCH;
	    var scan_end1 = this._window[scanp + best_len - 1];
	    var scan_end  = this._window[scanp + best_len];

	   
	    if(this._prev_length >= this._good_match){
	    	chain_length >>= 2;
	    }

	    do {
			matchp = cur_match;
	
			if(this._window[matchp + best_len]	!= scan_end  ||
			   this._window[matchp + best_len - 1]	!= scan_end1 ||
			   this._window[matchp]			!= this._window[scanp] ||
			   this._window[++matchp]			!= this._window[scanp + 1]) {
			    continue;
			}
	
			scanp += 2;
			matchp++;

			do {
			} while(this._window[++scanp] == this._window[++matchp] &&
				this._window[++scanp] == this._window[++matchp] &&
				this._window[++scanp] == this._window[++matchp] &&
				this._window[++scanp] == this._window[++matchp] &&
				this._window[++scanp] == this._window[++matchp] &&
				this._window[++scanp] == this._window[++matchp] &&
				this._window[++scanp] == this._window[++matchp] &&
				this._window[++scanp] == this._window[++matchp] &&
				scanp < strendp);
	
		      len = this._MAX_MATCH - (strendp - scanp);
		      scanp = strendp - this._MAX_MATCH;
	
		      if(len > best_len) {
				  this._match_start = cur_match;
				  best_len = len;
				  if(this._FULL_SEARCH) {
				      if(len >= this._MAX_MATCH){
				    	  break;
				      }
				  } else {
				      if(len >= this._nice_match){
				    	  break;
				      }
				  }
		
				  scan_end1  = this._window[scanp + best_len-1];
				  scan_end   = this._window[scanp + best_len];
		      }
	    } while((cur_match = this._prev[cur_match & this._WMASK]) > limit
		    && --chain_length != 0);

	    return best_len;
	},
	
	
	

	_SEND_CODE: function(c, tree) {
	    this._send_bits(tree[c].fc, tree[c].dl);
	},

	
	_D_CODE: function(dist) {
	    return (dist < 256 ? this._dist_code[dist]
		    : this._dist_code[256 + (dist>>7)]) & 0xff;
	},

	
	_SMALLER: function(tree, n, m) {
	    return tree[n].fc < tree[m].fc ||
	      (tree[n].fc == tree[m].fc && this._depth[n] <= this._depth[m]);
	},


	_read_buff: function(buff, offset, n) {
	    var i;
	    for(i = 0; i < n && this._deflate_pos < this._deflate_data.length; i++)
		buff[offset + i] =
		    this._deflate_data.charCodeAt(this._deflate_pos++) & 0xff;
	    return i;
	},
	
	_build_tree: function(desc) { 
	    var tree	= desc.dyn_tree;
	    var stree	= desc.static_tree;
	    var elems	= desc.elems;
	    var n; 
	    var m;		
	    var max_code = -1;	
	    var node = elems;

	    this._heap_len = 0;
	    this._heap_max = this._HEAP_SIZE;

	    for(n = 0; n < elems; n++) {
			if(tree[n].fc != 0) {
			    this._heap[++this._heap_len] = max_code = n;
			    this._depth[n] = 0;
			} else{
			    tree[n].dl = 0;
			}
	    }

	    while(this._heap_len < 2) {
			var xnew = this._heap[++this._heap_len] = (max_code < 2 ? ++max_code : 0);
			tree[xnew].fc = 1;
			this._depth[xnew] = 0;
			this._opt_len--;
			if(stree != null){
			    this._static_len -= stree[xnew].dl;
			}
			
	    }
	    desc.max_code = max_code;

	 
	    for(n = this._heap_len >> 1; n >= 1; n--){
	    	this._pqdownheap(tree, n);
	    }
	 
	    do {
	    	n = this._heap[this._SMALLEST];
			this._heap[this._SMALLEST] = this._heap[this._heap_len--];
			this._pqdownheap(tree, this._SMALLEST);
	
			m = this._heap[this._SMALLEST]; 
	
			
			this._heap[--this._heap_max] = n;
			this._heap[--this._heap_max] = m;
	
		
			tree[node].fc = tree[n].fc + tree[m].fc;
			if(this._depth[n] > this._depth[m] + 1){
			    this._depth[node] = this._depth[n];
			}
			else{
			    this._depth[node] = this._depth[m] + 1;
			}
			tree[n].dl = tree[m].dl = node;
	
			
			this._heap[this._SMALLEST] = node++;
			this._pqdownheap(tree, this._SMALLEST);

	    } while(this._heap_len >= 2);

	    this._heap[--this._heap_max] = this._heap[this._SMALLEST];

	    this._gen_bitlen(desc);

	   
	    this._gen_codes(tree, max_code);
	},


	_scan_tree: function(tree,
			       max_code) { 
	    var n;			
	    var prevlen = -1;		
	    var curlen;			
	    var nextlen = tree[0].dl;	
	    var count = 0;		
	    var max_count = 7;		
	    var min_count = 4;	

	    if(nextlen == 0) {
		max_count = 138;
		min_count = 3;
	    }
	    tree[max_code + 1].dl = 0xffff;

	    for(n = 0; n <= max_code; n++) {
		curlen = nextlen;
		nextlen = tree[n + 1].dl;
		if(++count < max_count && curlen == nextlen)
		    continue;
		else if(count < min_count)
		    this._bl_tree[curlen].fc += count;
		else if(curlen != 0) {
		    if(curlen != prevlen)
			this._bl_tree[curlen].fc++;
		    this._bl_tree[this._REP_3_6].fc++;
		} else if(count <= 10)
		    this._bl_tree[this._REPZ_3_10].fc++;
		else
		    this._bl_tree[this._REPZ_11_138].fc++;
		count = 0; prevlen = curlen;
		if(nextlen == 0) {
		    max_count = 138;
		    min_count = 3;
		} else if(curlen == nextlen) {
		    max_count = 6;
		    min_count = 3;
		} else {
		    max_count = 7;
		    min_count = 4;
		}
	    }
	},

	_send_tree: function(tree, 
			   max_code) { 
	    var n;			
	    var prevlen = -1;
	    var curlen;			
	    var nextlen = tree[0].dl;	
	    var count = 0;		
	    var max_count = 7;	
	    var min_count = 4;	

	    if(nextlen == 0) {
	      max_count = 138;
	      min_count = 3;
	    }

	    for(n = 0; n <= max_code; n++) {
			curlen = nextlen;
			nextlen = tree[n+1].dl;
			if(++count < max_count && curlen == nextlen) {
			    continue;
			} else if(count < min_count) {
			    do {
			    	this._SEND_CODE(curlen, this._bl_tree); 
			    	} while(--count != 0);
			} else if(curlen != 0) {
			    if(curlen != prevlen) {
					this._SEND_CODE(curlen, this._bl_tree);
					count--;
			    }
			    this._SEND_CODE(this._REP_3_6, this._bl_tree);
			    this._send_bits(count - 3, 2);
			} else if(count <= 10) {
			    this._SEND_CODE(this._REPZ_3_10, this._bl_tree);
			    this._send_bits(count-3, 3);
			} else {
			    this._SEND_CODE(this._REPZ_11_138, this._bl_tree);
			    this._send_bits(count-11, 7);
			}
			count = 0;
			prevlen = curlen;
			if(nextlen == 0) {
			    max_count = 138;
			    min_count = 3;
			} else if(curlen == nextlen) {
			    max_count = 6;
			    min_count = 3;
			} else {
			    max_count = 7;
			    min_count = 4;
			}
	    }
	},


	_build_bl_tree: function() {
	    var max_blindex; 

	    
	    this._scan_tree(this._dyn_ltree, this._l_desc.max_code);
	    this._scan_tree(this._dyn_dtree, this._d_desc.max_code);

	   
	    this._build_tree(this._bl_desc);
	  

	    for(max_blindex = this._BL_CODES-1; max_blindex >= 3; max_blindex--) {
	    	if(this._bl_tree[this._bl_order[max_blindex]].dl != 0){
	    		break;
	    	}
	    }
	   
	    this._opt_len += 3*(max_blindex+1) + 5+5+4;

	    return max_blindex;
	},

	_send_all_trees: function(lcodes, dcodes, blcodes) {
	    var rank; 
	    this._send_bits(lcodes-257, 5); 
	    this._send_bits(dcodes-1,   5);
	    this._send_bits(blcodes-4,  4);
	    for(rank = 0; rank < blcodes; rank++) {
	    	this._send_bits(this._bl_tree[this._bl_order[rank]].dl, 3);
	    }

	    
	    this._send_tree(this._dyn_ltree,lcodes-1);

	   
	    this._send_tree(this._dyn_dtree,dcodes-1);
	},
	

	_send_bits: function(
		value,	
		length) {	
	  
	    if(this._bi_valid > this._Buf_size - length) {
			this._bi_buf |= (value << this._bi_valid);
			this._put_short(this._bi_buf);
			this._bi_buf = (value >> (this._Buf_size - this._bi_valid));
			this._bi_valid += length - this._Buf_size;
	    } else {
			this._bi_buf |= value << this._bi_valid;
			this._bi_valid += length;
	    }
	},
	

	
	_bi_windup: function() {
	    if(this._bi_valid > 8) {
	    	this._put_short(this._bi_buf);
	    } else if(this._bi_valid > 0) {
	    	this._put_byte(this._bi_buf);
	    }
	    this._bi_buf = 0;
	    this._bi_valid = 0;
	},
	
	
	_put_short: function(w) {
	    w &= 0xffff;
	    if(this._outoff + this._outcnt < this._OUTBUFSIZ - 2) {
			this._outbuf[this._outoff + this._outcnt++] = (w & 0xff);
			this._outbuf[this._outoff + this._outcnt++] = (w >>> 8);
	    } else {
			this._put_byte(w & 0xff);
			this._put_byte(w >>> 8);
	    }
	},

	_put_byte: function(c) {
	    this._outbuf[this._outoff + this._outcnt++] = c;
	    if(this._outoff + this._outcnt == this._OUTBUFSIZ){
	    	this._qoutbuf();
	    }
	},
	

	_compress_block: function(
		ltree,	
		dtree) {
	    var dist;	
	    var lc;		
	    var lx = 0;	
	    var dx = 0;		
	    var fx = 0;		
	    var flag = 0;	
	    var code;	
	    var extra;	

	    if(this._last_lit != 0){
	    	do {
				if((lx & 7) == 0){
					flag = this._flag_buf[fx++];
				}
				lc = this._l_buf[lx++] & 0xff;
				if((flag & 1) == 0) {
				    this._SEND_CODE(lc, ltree); 
				} else {
				 
				    code = this._length_code[lc];
				    this._SEND_CODE(code+this._LITERALS+1, ltree);
				    extra = this._extra_lbits[code];
				    if(extra != 0) {
						lc -= this._base_length[code];
						this._send_bits(lc, extra); 
				    }
				    dist = this._d_buf[dx++];
				   
				    code = this._D_CODE(dist);
		
				    this._SEND_CODE(code, dtree);	 
				    extra = this._extra_dbits[code];
				    if(extra != 0) {
						dist -= this._base_dist[code];
						this._send_bits(dist, extra); 
				    }
				} 
				flag >>= 1;
	    } while(lx < this._last_lit);
	}

	    this._SEND_CODE(this._END_BLOCK, ltree);
	},

	_init_block: function() {
	    var n; 
	  
	    for(n = 0; n < this._L_CODES;  n++){
	    	this._dyn_ltree[n].fc = 0;
	    }
	    for(n = 0; n < this._D_CODES;  n++){
	    	this._dyn_dtree[n].fc = 0;
	    }
	    for(n = 0; n < this._BL_CODES; n++){
	    	this._bl_tree[n].fc = 0;
	    }

	    this._dyn_ltree[this._END_BLOCK].fc = 1;
	    this._opt_len = this._static_len = 0;
	    this._last_lit = this._last_dist = this._last_flags = 0;
	    this._flags = 0;
	    this._flag_bit = 1;
	},

	
	_pqdownheap: function(tree,	
			k) {	
	    var v = this._heap[k];
	    var j = k << 1;	

	    while(j <= this._heap_len) {
			
			if(j < this._heap_len &&
			   this._SMALLER(tree, this._heap[j + 1], this._heap[j])){
				j++;
			}
	
			
			if(this._SMALLER(tree, v, this._heap[j])){
				break;
			}
	
			
			this._heap[k] = this._heap[j];
			k = j;
	
			j <<= 1;
	    }
	    this._heap[k] = v;
	},


	_gen_bitlen: function(desc) { 
	    var tree		= desc.dyn_tree;
	    var extra		= desc.extra_bits;
	    var base		= desc.extra_base;
	    var max_code	= desc.max_code;
	    var max_length	= desc.max_length;
	    var stree		= desc.static_tree;
	    var h;		
	    var n;
	    var m;	
	    var bits;		
	    var xbits;		
	    var f;		
	    var overflow = 0;

	    for(bits = 0; bits <= this._MAX_BITS; bits++){
	    	this._bl_count[bits] = 0;
	    }

	    tree[this._heap[this._heap_max]].dl = 0; 

	    for(h = this._heap_max + 1; h < this._HEAP_SIZE; h++) {
			n = this._heap[h];
			bits = tree[tree[n].dl].dl + 1;
			if(bits > max_length) {
			    bits = max_length;
			    overflow++;
			}
			tree[n].dl = bits;
			
	
			if(n > max_code){
				continue;
			}
	
			this._bl_count[bits]++;
			xbits = 0;
			if(n >= base){
				xbits = extra[n - base];
			}
			f = tree[n].fc;
			this._opt_len += f * (bits + xbits);
			if(stree != null){
				this._static_len += f * (stree[n].dl + xbits);
			}
	    }
	    if(overflow == 0){
	    	return;
	    }


	    do {
			bits = max_length - 1;
			while(this._bl_count[bits] == 0){
				bits--;
			}
			this._bl_count[bits]--;		
			this._bl_count[bits + 1] += 2;
			this._bl_count[max_length]--;
			
			overflow -= 2;
	    } while(overflow > 0);

	    
	    for(bits = max_length; bits != 0; bits--) {
			n = this._bl_count[bits];
			while(n != 0) {
			    m = this._heap[--h];
			    if(m > max_code){
			    	continue;
			    }
			    if(tree[m].dl != bits) {
					this._opt_len += (bits - tree[m].dl) * tree[m].fc;
					tree[m].fc = bits;
			    }
			    n--;
			}
	    }
	},
	

	_bi_reverse: function(
		code,	
		len) {	
	    var res = 0;
	    do {
			res |= code & 1;
			code >>= 1;
			res <<= 1;
	    } while(--len > 0);
	    return res >> 1;
	},
	
	_gen_codes: function(tree,
			   max_code) {	
	    var next_code = new Array(this._MAX_BITS+1); 
	    var code = 0;	
	    var bits;			
	    var n;		

	   
	    for(bits = 1; bits <= this._MAX_BITS; bits++) {
		code = ((code + this._bl_count[bits-1]) << 1);
		next_code[bits] = code;
	    }

	 
	    for(n = 0; n <= max_code; n++) {
		var len = tree[n].dl;
		if(len == 0)
		    continue;
	
		tree[n].fc = this._bi_reverse(next_code[len]++, len);
	    }
	},
	_qoutbuf: function() {
	    if(this._outcnt != 0) {
		var q, i;
		q = this._new_queue();
		if(this._qhead == null)
		    this._qhead = this._qtail = q;
		else
		    this._qtail = this._qtail.next = q;
		q.len = this._outcnt - this._outoff;
		for(i = 0; i < q.len; i++)
		    q.ptr[i] = this._outbuf[this._outoff + i];
		this._outcnt = this._outoff = 0;
	    }
	},
	
	_new_queue: function() {
	    var p;

	    if(this._free_queue != null)
	    {
			p = this._free_queue;
			this._free_queue = this._free_queue.next;
	    }
	    else{
	    	p = new _DeflateBuffer(this._OUTBUFSIZ);
	    }
	    p.next = null;
	    p.len = p.off = 0;

	    return p;
	},
	
	destroy: function() {
	    this._free_queue = null;
	    this._qhead = null;
	    this._qtail = null;
	    this._outbuf = null;
	    this._window = null;
	    this._d_buf = null;
	    this._l_buf = null;
	    this._prev = null;
	    this._dyn_ltree = null;
	    this._dyn_dtree = null;
	    this._static_ltree = null;
	    this._static_dtree = null;
	    this._bl_tree = null;
	    this._l_desc = null;
	    this._d_desc = null;
	    this._bl_desc = null;
	    this._bl_count = null;
	    this._heap = null;
	    this._depth = null;
	    this._length_code = null;
	    this._dist_code = null;
	    this._base_length = null;
	    this._base_dist = null;
	    this._flag_buf = null;
	}
	
});